package com.peng.sort.merge;

import com.peng.utils.TestUtils;

import java.util.Arrays;

import static com.peng.utils.TestUtils.printArray;

/*

求数组小和问题：
在一个数组中，一个数左边比它小的数的总和，叫数的小和，所有数的小和累加起来，叫数组小和。求数组小和。
例子： [1,3,4,2,5]
1左边比1小的数：没有
3左边比3小的数：1
4左边比4小的数：1、3
2左边比2小的数：1
5左边比5小的数：1、3、4、 2
所以数组的小和为1+1+3+1+1+3+4+2=16

 */
public class Test01_SmallSum {

    public static void main(String[] args) {
        int testTime = 500000;
        int maxSize = 100;
        int maxValue = 100;
        boolean succeed = true;
        for (int i = 0; i < testTime; i++) {
            int[] arr1 = TestUtils.generateRandomArray(maxSize, maxValue);
            int[] arr2 = TestUtils.copyArray(arr1);
            try {
                if (smallSum(arr1) != getSmallSumEasy(arr2)) {
                    succeed = false;
                    printArray(arr1);
                    printArray(arr2);
                    break;
                }
            } catch (Exception e) {
                System.out.println(Arrays.toString(arr1));
            }
        }
        System.out.println(succeed ? "Nice!" : "Fucking fucked!");

    }

    //对数器
    private static int getSmallSumEasy(int[] arr) {
        int res = 0;
        for (int i = 1; i < arr.length; i++) {
            for (int j = 0; j < i; j++) {
                res += arr[i] > arr[j] ? arr[j] : 0;
            }
        }
        return res;
    }

    public static int smallSum(int[] arr) {
        if (arr == null || arr.length < 2) {
            return 0;
        }
        return smallSum(arr, 0, arr.length - 1);
    }

    private static int smallSum(int[] arr, int L, int R) {
        if (L == R) {
            return 0;
        }
        int mid = L + ((R - L) >> 1);

        return smallSum(arr, L, mid)
                + smallSum(arr, mid + 1, R)
                + merge(arr, L, mid, R);
    }

    private static int merge(int[] arr, int L, int mid, int R) {
        int p1 = L;
        int p2 = mid + 1;
        int[] help = new int[R - L + 1];
        int i = 0;
        /*
        在归并排序的同时，进行小和累加
        找一个数的小和，
        常规做法：找一个数左边所有比它小的数
        转换思路，等同于： 找一个数右边有几个比它大的数
        通过归并排序的特征： 左右两组都是各自内部有序的数据特征，
        可以做到直接通过下标换算的方式计算出对一个数来说，此时的右组一共有多少个数是大于它的
         */
        int res = 0;
        while (p1 <= mid && p2 <= R) {
            if (arr[p1] < arr[p2]) {
                res += (R - p2 + 1) * arr[p1];
                help[i++] = arr[p1++];
            } else {
                help[i++] = arr[p2++];
            }
        }

        while (p1 <= mid) {
            help[i++] = arr[p1++];
        }
        while (p2 <= R) {
            help[i++] = arr[p2++];
        }


        for (int m = 0; m < help.length; m++) {
            arr[L + m] = help[m];
        }

        return res;
    }
    /*
    在归并排序的同时，进行小和累加
找一个数的小和累加，
常规做法：找一个数左边所有比它小的数
转换思路，等同于： 找一个数右边有几个比它大的数
通过归并排序的特征： 左右两组都是各自内部有序的数据特征，
可以做到直接通过下标换算的方式计算出对一个数来说，此时的右组一共有多少个数是大于它的




     */
}
